import { createTaskQueue, arrified, createStateNode, getTag, getRoot } from "../Misc";
import { updateNodeElement } from "../DOM"

const taskQueue = createTaskQueue();

let subTask = null;

let pendingCommit = null;

const commitAllWork = (fiber) => {
  // console.log(fiber.effects);
  /**
   * 循环 effets 数组 构建 DOM 节点树
   */
  fiber.effects.forEach((item) => {
    if (item.tag === 'class_componet') {
      item.stateNode.__fiber = item
    }

    if (item.effectTag === "delete") {
      console.log(item)
      item.parent.stateNode.removeChild(item.stateNode)
    } else if (item.effectTag === "update") {
      /**
       * 更新
       */
      if (item.type === item.alternate.type) {
        /**
         *  节点类型相同
         */
        updateNodeElement(item.stateNode, item, item.alternate)
      } else {
        /**
         * 节点类型不同
         * 不需要比对的
         */
        item.parent.stateNode.replaceChild(
          item.stateNode,
          item.alternate.stateNode
        )
      }
    } else if (item.effectTag === "placement") {
      // console.log(item.parent);
      /**
       * 向页面中追加节点
       */
      /**
       * 当前要追加的子节点
       */
      let fiber = item;
      /**
       * 当前要追加的子节点的父级
       */
      let parentFiber = item.parent;
      /**
       * 找到普通节点父级 排除组件父级
       * 因为组件父级是不能直接追加真实DOM节点的
       */
      while (
        parentFiber.tag === "class_componet" ||
        parentFiber.tag === "function_componet"
      ) {
        parentFiber = parentFiber.parent;
      }
      /**
       * 如果子节点是普通节点 找到父级 将子节点追加到父级中
       */
      if (fiber.tag === "host_componet") {
        // 普通节点
        parentFiber.stateNode.appendChild(fiber.stateNode);
      }
    }
  });
  /**
   * 备份旧的 fiber 节点对象
   * 根节点的fiber对象
   */
  fiber.stateNode.__rootFiberContainer = fiber;
};

// 获取任务队列当中，第一个任务的小任务
// 而不是指任务队列当中的第一个任务
const getFirstTask = () => {
  /**
   * 从任务队列中获取任务
   */
  const task = taskQueue.pop();
  // console.log(task)

  // 判断任务是从哪来的
  if (task.from === 'class_componet') {
    // 重新构建最外层节点的fiber对象
    const root = getRoot(task.instance)
    // console.log(root)
    task.instance.__fiber.partialState = task.partialState
    return {
      props: root.props,
      stateNode: root.stateNode,
      tag: 'host_root',
      effects: [],
      child: null,
      alternate: root
    }
  }
  /**
   * 返回最外层节点的fiber对象
   * {
   *  props: task.props,
   *  stateNode: task.dom,
   *  tag: 'host_root',
   *  effects: [],
   *  child: null,
   * }
   */
  return {
    // type: '', // 节点类型（元素，文本，组件）（具体的类型）
    props: task.props, // 节点属性
    stateNode: task.dom, // 节点 DOM对象 | 组件实例对象
    tag: "host_root", // 节点标记（对具体类型的分类 hostRoot || hostComponent || classComponent || functionComponent。根节点不需要通过getTag来获取，因为根节点永远是id为root的节点，它的值永远是host_root
    effects: [], // 数组，存储需要更改的fiber对象
    // effectTag: '', // 当前Fiber要被执行的操作（新增，删除，修改），根节点不需要
    // parent: '', // 当前Fiber的父级Fiber
    child: null, // 当前Fiber的子级Fiber
    // sibling: '', // 当前Fiber的下一个兄弟Fiber
    alternate: task.dom.__rootFiberContainer, // Fiber备份 fiber比对时使用
  };
};

const reconcileChildren = (fiber, children) => {
  /**
   * children可能是对象，也可能是数组
   * 将children转成数组
   */
  const arrifiedChildren = arrified(children);
  /**
   * 循环 children 使用的索引
   */
  let index = 0;
  /**
   * children 数组中元素的个数
   */
  let numberOfElements = arrifiedChildren.length;
  /**
   * 循环过程中的循环项 就是子节点的 virtualDOM 对象
   */
  let element = null;
  /**
   * 子级 fiber 对象
   */
  let newFiber = null; // 子节点
  /**
   * 上一个兄弟 fiber 对象
   */
  let preFiber = null;

  let alternate = null;

  if (fiber.alternate && fiber.alternate.child) {
    alternate = fiber.alternate.child; // 第一个子节点
  }

  while (index < numberOfElements || alternate) {
    /**
     * 子级 virtualDOM 对象
     */
    element = arrifiedChildren[index];
    if (!element && alternate) {
      // console.log('删除操作==>', element, alternate)
      /**
       * 删除操作
       */
      alternate.effectTag = "delete"
      fiber.effects.push(alternate)
    } else if (element && alternate) {
      /**
       * 更新
       */
      newFiber = {
        type: element.type,
        props: element.props,
        tag: getTag(element),
        effects: [],
        effectTag: "update",
        parent: fiber,
        alternate
      }
      if (element.type === alternate.type) {
        /**
         * 类型相同
         */
        newFiber.stateNode = alternate.stateNode
      } else {
        /**
         * 类型不同
         * 直接创建新的节点替换老的节点
         */
        // 为fiber节点添加DOM对象或组件实例对象
        newFiber.stateNode = createStateNode(newFiber)
      }
    } else if (element && !alternate) {
      /**
       * 初始渲染
       */
      /**
       * 子级 fiber 对象
       */
      newFiber = {
        type: element.type,
        props: element.props,
        tag: getTag(element),
        effects: [],
        effectTag: "placement",
        stateNode: null,
        parent: fiber,
      };
      /**
       * 为fiber节点添加DOM对象或组件实例对象
       */
      newFiber.stateNode = createStateNode(newFiber);
    }

    if (index === 0) {
      fiber.child = newFiber;
    } else if (element) {
      preFiber.sibling = newFiber;
    }

    if (alternate && alternate.sibling) {
      alternate = alternate.sibling;
    } else {
      alternate = null;
    }

    // 更新
    preFiber = newFiber;

    index++;
  }
};

const executeTask = (fiber) => {
  /**
   * 构建子级fiber对象
   */
  if (fiber.tag === "class_componet") {
    if (fiber.stateNode.__fiber && fiber.stateNode.__fiber.partialState) {
      fiber.stateNode.state = {
        ...fiber.stateNode.state,
        ...fiber.stateNode.__fiber.partialState
      }
    }

    reconcileChildren(fiber, fiber.stateNode.render());
  }
  if (fiber.tag === "function_componet") {
    reconcileChildren(fiber, fiber.stateNode(fiber.props));
  } else {
    // 普通节点
    // 调用方法构建子节点
    reconcileChildren(fiber, fiber.props.children);
  }

  /**
   * 如果子级存在 返回子级
   * 将这个子级当做父级 构建这个父级下的子级
   */
  if (fiber.child) {
    return fiber.child;
  }

  let currentExecutelyFiber = fiber;

  while (currentExecutelyFiber.parent) {
    currentExecutelyFiber.parent.effects =
      currentExecutelyFiber.parent.effects.concat(
        currentExecutelyFiber.effects.concat([currentExecutelyFiber])
      );
    if (currentExecutelyFiber.sibling) {
      // 是否有同级
      return currentExecutelyFiber.sibling;
    }
    currentExecutelyFiber = currentExecutelyFiber.parent;
  }
  pendingCommit = currentExecutelyFiber;
  // console.log(fiber);
  // console.log(currentExecutelyFiber);
};

const workLoop = (deadline) => {
  /**
   * 如果子任务不存在就去获取子任务
   */
  if (!subTask) {
    subTask = getFirstTask();
  }
  /**
   * 如果任务存在并且浏览器有空余时间（浏览器的空余时间大于1ms）就调用
   * executeTask方法执行任务 接受任务 返回新的任务
   */
  while (subTask && deadline.timeRemaining() > 1) {
    subTask = executeTask(subTask);
  }
  if (pendingCommit) {
    commitAllWork(pendingCommit);
  }
};

const performTask = (deadline) => {
  /**
   * 执行任务
   */
  workLoop(deadline);
  /**
   * 判断任务是否存在
   * 判断任务队列中是否还有任务没有执行
   * 再一次告诉浏览器在空闲时间执行任务
   */
  if (subTask || !taskQueue.isEmpty()) {
    requestIdleCallback(performTask);
  }
};

export const render = (element, dom) => {
  // console.log(element, dom)
  /**
   * 1.向任务队列中添加任务
   * 2.指定在浏览器空闲时执行任务
   */

  /**
   * 任务就是通过vdom对象构建fiber对象
   */
  taskQueue.push({
    dom, // 父级
    props: { children: element }, // 子级
  });
  /**
   * 指定浏览器在空闲时间执行任务
   * requestIdleCallback接收一个参数，这个参数是一个函数，
   * 当浏览器空闲的时候就会调用这个函数
   */
  requestIdleCallback(performTask);
};

export const scheduleUpdate = (instance, partialState) => {
  console.log('组件的state更新', partialState)
  taskQueue.push({
    from: "class_componet",
    instance,
    partialState
  })
  requestIdleCallback(performTask)
}